using System;
using System.Collections.Generic;
using System.Text;
using ALib;

namespace TradingPlatform
{
    public class OrderManagementDb
    {
        // ticker name, order id, shares.
        private readonly Dictionary<string, Dictionary<int, OrderInfo>> _orderSet;

        public Dictionary<string, Dictionary<int, OrderInfo>> OrderSet
        {
            get { return _orderSet; }
        }

        public List<int> AliveOrderIdList
        {
            get
            {
                List<int> result = new List<int>();
                foreach (KeyValuePair<string, Dictionary<int, OrderInfo>> tickerName_OrderInfoList in _orderSet)
                {
                    foreach (KeyValuePair<int, OrderInfo> orderId_OrderInfo in tickerName_OrderInfoList.Value)
                    {
                        if (orderId_OrderInfo.Value.Alive)
                        {
                            result.Add(orderId_OrderInfo.Key);   
                        }
                    }
                }
                return result;
            }
        }

        private readonly Dictionary<string, double> _openPositionList;

        public Dictionary<string, double> OpenPositionList
        {
            get { return _openPositionList; }
        }

        public string OrderSetString
        {
            get
            {
                StringBuilder result = new StringBuilder();

                foreach (KeyValuePair<string, Dictionary<int, OrderInfo>> tickerName_OrderInfoList in _orderSet)
                {
                    string tickerName = tickerName_OrderInfoList.Key;
                    foreach (KeyValuePair<int, OrderInfo> orderId_OrderInfo in tickerName_OrderInfoList.Value)
                    {
                        if (orderId_OrderInfo.Value.Alive)
                        {
                            int orderId = orderId_OrderInfo.Key;
                            string orderInfo = orderId_OrderInfo.Value.ToString();
                            string outputString = string.Format(@"{0},{1},{2}", tickerName, orderId, orderInfo);
                            result.AppendLine(outputString);   
                        }
                    }
                }
                return result.ToString();
            }
        }

        public string OpenPositionString
        {
            get
            {
                StringBuilder result = new StringBuilder();
                foreach (KeyValuePair<string, double> tickerName_OpenPosition in _openPositionList)
                {
                    string outputString = string.Format(@"{0},{1}", tickerName_OpenPosition.Key, tickerName_OpenPosition.Value);
                    result.AppendLine(outputString);
                }
                return result.ToString();
            }
        }

        public OrderManagementDb()
        {
            _orderSet = new Dictionary<string, Dictionary<int, OrderInfo>>();
            _openPositionList = new Dictionary<string, double>();
        }

        public void SendOrder(string tickerName, int orderId, double price, double quantity)
        {
            GoodValue.Assert(price > 0 || double.IsNaN(price));
            GoodValue.Assert(quantity != 0);
            GoodValue.AssertDouble(quantity);

            GoodValue.Assert(!_orderSet.ContainsKey(tickerName) || !_orderSet[tickerName].ContainsKey(orderId));
            
            if (!_orderSet.ContainsKey(tickerName))
            {
                _orderSet.Add(tickerName, new Dictionary<int, OrderInfo>());
            }
            _orderSet[tickerName].Add(orderId, new OrderInfo(price, quantity));

            AssertIdenticalOrderId();
        }

        public void CancelOrder(string tickerName, int orderId)
        {
            if (_orderSet.ContainsKey(tickerName)&&_orderSet[tickerName].ContainsKey(orderId))
            {
                _orderSet[tickerName][orderId].Cancelled = true;
            }
        }

        public void CancelOrder(int orderId)
        {
            string tickerName = GetTickerName(orderId);
            CancelOrder(tickerName, orderId);
        }

        public void CancelAllOrders()
        {
            foreach (int aliveOrderId in AliveOrderIdList)
            {
                CancelOrder(aliveOrderId);
            }
        }

        //public void FillOrder(string tickerName, int orderId, double filled)
        //{
        //    GoodValue.Assert(filled >= 0);

        //    filled = _originalOrderSet[tickerName][orderId].Quantity > 0 ? Math.Abs(filled) : -Math.Abs(filled);

        //    if (_orderSet.ContainsKey(tickerName) && _orderSet[tickerName].ContainsKey(orderId))
        //    {
        //        GoodValue.Assert(_orderSet[tickerName][orderId].Quantity * filled >= 0);
        //        GoodValue.Assert(Math.Abs(_orderSet[tickerName][orderId].Quantity) >= Math.Abs(filled));
        //        _orderSet[tickerName][orderId].Quantity = _orderSet[tickerName][orderId].Quantity - filled;
        //        if (_orderSet[tickerName][orderId].Quantity == 0)
        //        {
        //            CancelOrder(tickerName, orderId);
        //        }
        //    }

        //    AdjustOpenPosition(tickerName, filled);
        //}

        //public void FillOrder(int orderId, double filled)
        //{
        //    GoodValue.Assert(filled >= 0);
        //    string tickerName = GetTickerName(orderId);
        //    FillOrder(tickerName, orderId, filled);
        //}

        public void FillOrder_TotalRemaining(string tickerName, int orderId, double totalRemaining)
        {
            GoodValue.Assert(totalRemaining >= 0);
            if (_orderSet.ContainsKey(tickerName)&&_orderSet[tickerName].ContainsKey(orderId))
            {
                totalRemaining = _orderSet[tickerName][orderId].Quantity > 0 ? Math.Abs(totalRemaining) : -Math.Abs(totalRemaining);

                GoodValue.Assert(_orderSet[tickerName][orderId].Remaining * totalRemaining >= 0);
                GoodValue.Assert(Math.Abs(_orderSet[tickerName][orderId].Remaining) >= Math.Abs(totalRemaining));

                double filled = _orderSet[tickerName][orderId].Remaining - totalRemaining;
                _orderSet[tickerName][orderId].Remaining = totalRemaining;

                AdjustOpenPosition(tickerName, filled);   
            }
        }

        public void FillOrder_TotalRemaining(int orderId, double totalRemaining)
        {
            GoodValue.Assert(totalRemaining >= 0);
            string tickerName = GetTickerName(orderId);
            FillOrder_TotalRemaining(tickerName, orderId, totalRemaining);
        }

        private void AssertIdenticalOrderId()
        {
            List<int> orderIdList = new List<int>();
            foreach (KeyValuePair<string, Dictionary<int, OrderInfo>> tickerName_OrderList in _orderSet)
            {
                foreach (KeyValuePair<int, OrderInfo> orderId_OrderInfo in tickerName_OrderList.Value)
                {
                    GoodValue.Assert(!orderIdList.Contains(orderId_OrderInfo.Key));
                    orderIdList.Add(orderId_OrderInfo.Key);
                }
            }
        }

        private string GetTickerName(int orderId)
        {
            string result = string.Empty;
            foreach (KeyValuePair<string, Dictionary<int, OrderInfo>> tickerName_OrderList in _orderSet)
            {
                if (tickerName_OrderList.Value.ContainsKey(orderId))
                {
                    result = tickerName_OrderList.Key;
                    break;
                }
            }
            GoodValue.Assert(result != string.Empty);
            return result;
        }

        private void AdjustOpenPosition(string tickerName, double filled)
        {
            // Adjust open position.
            if (!_openPositionList.ContainsKey(tickerName))
            {
                _openPositionList.Add(tickerName, 0);
            }
            _openPositionList[tickerName] = _openPositionList[tickerName] + filled;
            if (_openPositionList[tickerName] == 0)
            {
                _openPositionList.Remove(tickerName);
            }
        }

        public void FlatOpenPosition()
        {
            _openPositionList.Clear();
        }
    }
}
